package com.alogic.xscript.jwt;

import com.alogic.xscript.ExecuteWatcher;
import com.alogic.xscript.Logiclet;
import com.alogic.xscript.LogicletContext;
import com.alogic.xscript.doc.XsObject;
import com.alogic.xscript.doc.json.JsonObject;
import com.alogic.xscript.plugins.Segment;
import com.anysoft.util.Properties;
import com.anysoft.util.PropertiesConstants;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT工具
 *
 * @since 1.6.14.13 [2021708 duanyy] <br>
 */
public class JWToken extends Segment {
    public static final String DATA_ID = "data";
    protected static final String DEFAULT_KEY = "alogic";
    protected String $key = DEFAULT_KEY;

    public JWToken(String tag, Logiclet p) {
        super(tag, p);
        registerModule("jwt-new",New.class);
        registerModule("jwt-load",Load.class);
    }

    @Override
    public void configure(Properties p){
        super.configure(p);
        $key = PropertiesConstants.getRaw(p,"key",$key);
    }

    public static class New extends JWToken{
        protected String $id;
        protected String $ttl = "0";
        public New(String tag, Logiclet p) {
            super(tag, p);
        }

        @Override
        public void configure(Properties p){
            super.configure(p);
            $id = PropertiesConstants.getRaw(p,"id",$id);
            $ttl = PropertiesConstants.getRaw(p,"ttl",$ttl);
        }

        @Override
        protected void onExecute(XsObject root, XsObject current, LogicletContext ctx, ExecuteWatcher watcher) {
            String id = PropertiesConstants.transform(ctx,$id,"");
            if (StringUtils.isNotEmpty(id)) {
                Map<String, Object> jsonData = new HashMap<String, Object>();
                try {
                    XsObject doc = new JsonObject("root", jsonData);
                    super.onExecute(doc, doc, ctx, watcher);
                    JWTCreator.Builder builder = JWT.create();
                    long ttl = PropertiesConstants.getLong(ctx,$ttl,0);
                    if (ttl > 0){
                        builder.withExpiresAt(
                            new Date(System.currentTimeMillis() + ttl * 60 * 1000L));
                    }
                    builder.withClaim(DATA_ID, jsonData);
                    Algorithm algorithm = Algorithm.HMAC256(PropertiesConstants.transform(ctx, $key, DEFAULT_KEY));
                    ctx.SetValue(id, builder.sign(algorithm));
                }catch (JWTCreationException ex){
                    log("Can not create jwt token.","error");
                    log(ex.getMessage(),"error");
                    ctx.SetValue(id,"0");
                }
            }
        }
    }

    public static class Load extends JWToken{
        protected String $token;
        public Load(String tag, Logiclet p) {
            super(tag, p);
        }

        @Override
        public void configure(Properties p){
            super.configure(p);
            $token = PropertiesConstants.getRaw(p,"token",$token);
        }

        @Override
        protected void onExecute(XsObject root, XsObject current, LogicletContext ctx, ExecuteWatcher watcher) {
            String token = PropertiesConstants.transform(ctx,$token,null);
            if  (StringUtils.isNotEmpty(token)){
                try {
                    Algorithm algorithm = Algorithm.HMAC256(PropertiesConstants.transform(ctx, $key, DEFAULT_KEY));
                    JWTVerifier verifier = JWT.require(algorithm).build();
                    DecodedJWT jwt = verifier.verify(token);
                    Claim claim = jwt.getClaim(DATA_ID);
                    if (claim != null){
                        Map<String,Object> map = claim.asMap();
                        if (map != null){
                            XsObject doc = new JsonObject("root", map);
                            super.onExecute(doc, doc, ctx, watcher);
                        }
                    }
                } catch (JWTVerificationException ex){
                    log(ExceptionUtils.getStackTrace(ex));
                }
            }
        }
    }
}